package com.xixi.core.rpc.protocol;

import com.xixi.core.rpc.model.RpcRequest;
import com.xixi.core.rpc.model.RpcResponse;
import com.xixi.core.rpc.serializer.CustomSerializer;
import com.xixi.core.rpc.serializer.CustomSerializerFactory;
import io.vertx.core.buffer.Buffer;

import java.io.IOException;

/**
 * @author: zxh
 * @date: 2024/8/26 11:30
 * @description: 协议消息解码器
 */
public class ProtocolMessageDecoder {

	/**
	 * 解码
	 * @param buffer 待解码的字节流
	 * @return 解码完成的消息对象
	 * @throws IOException
	 */
	public static ProtocolMessage<?> decode(Buffer buffer) throws IOException {
		// 分别从指定位置读出Buffer
		ProtocolMessage.Header header = new ProtocolMessage.Header();
		byte magic = buffer.getByte(0);
		// 校验魔数
		if (magic != ProtocolConstant.PROTOCOL_MAGIC) {
			throw new RuntimeException("消息 magic 非法");
		}
		header.setMagic(magic);
		header.setVersion(buffer.getByte(1));
		header.setSerializer(buffer.getByte(2));
		header.setType(buffer.getByte(3));
		header.setStatus(buffer.getByte(4));
		header.setRequestId(buffer.getLong(5));
		header.setBodyLength(buffer.getInt(13));
		// 解决粘包问题，只读指定长度的数据
		byte[] bodyBytes = buffer.getBytes(17, 17 + header.getBodyLength());
		// 解析消息体
		ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer());
		if (serializerEnum == null) {
			throw new RuntimeException("序列化消息的协议不存在");
		}
		CustomSerializer serializer = CustomSerializerFactory.getInstance(serializerEnum.getValue());
		ProtocolMessageTypeEnum messageTypeEnum = ProtocolMessageTypeEnum.getEnumByKey(header.getType());
		if (messageTypeEnum == null) {
			throw new RuntimeException("序列化消息的类型不存在");
		}
		switch (messageTypeEnum) {
			case REQUEST:
				RpcRequest request = serializer.deserialize(bodyBytes, RpcRequest.class);
				return new ProtocolMessage<>(header, request);
			case RESPONSE:
				RpcResponse response = serializer.deserialize(bodyBytes, RpcResponse.class);
				return new ProtocolMessage<>(header, response);
			case HEART_BEAT:
			case OTHERS:
			default:
				throw new RuntimeException("暂不支持该消息类型");
		}
	}
}
